const Api = {
	uri: "/api",
	// version: "v1",
	version: "",

	prefix: null,
};

Api.prefix = Api.version ? `${Api.uri}/${Api.version}` : Api.uri;

function processApiResponse(response) {
	if (!response.ok) {
		throw new Error("请求失败");
	}


	// 检查返回数据是否为json
	const contentType = response.headers.get("content-type") || "";
	const isJsonResponse = contentType.includes("application/json");

	if (!isJsonResponse) {
		const message = `接口(${response.url})返回数据类型错误：${contentType}`;
		throw new Error(message);
	}

	return response.json();
}

function processApiResult(result, resolve, reject) {
	if (result.success) {
		if (result.data.main) {
			resolve(result.data.main);
		} else {
			resolve(result.data);
		}
	} else {
		reject(result.message);
	}
}

class WebDao {
	static #objToPara = data => {
		if (!data || "object" !== typeof(data) 
				|| Array.isArray(data)) {
			return "";
		}


		let temp = "";

		for (let key in data) {
			let value = data[key];

			temp += `&${key}=${value}`;
		}

		return temp ? temp.substr(1) : temp;
	};

	static #formatUrl = (url, data) => {
		if (!url) return;

		if (data) {
			const para = this.#objToPara(data);

			if (url.includes("?")) {
				url = `${url}&${para}`;
			} else {
				url = `${url}?${para}`;
			}
		}

		return url;
	};

	static getJson(url, data) {
		return new Promise((resolve, reject) => {
			fetch(this.#formatUrl(url, data))
			.then(response => processApiResponse(response))
			.then(result => processApiResult(result, resolve, reject))
			.catch(error => reject(error));
		});
	}

	static send(url, data, method="post") {
		return new Promise((resolve, reject) => {
			fetch(this.#formatUrl(url), {
				method: method,
				headers: {
					"Content-Type": "application/x-www-form-urlencoded",
				},
				body: this.#objToPara(data),
			})
			.then(response => processApiResponse(response))
			.then(result => processApiResult(result, resolve, reject))
			.catch(error => reject(error));
		});
	}

	static sendJson(url, data, method="post") {
		return new Promise((resolve, reject) => {
			fetch(this.#formatUrl(url), {
				method: method,
				headers: {
					"Content-Type": "application/json",
				},
				body: JSON.stringify(data),
			})
			.then(response => processApiResponse(response))
			.then(result => processApiResult(result, resolve, reject))
			.catch(error => reject(error));
		});
	}

	static postFile(url, formData) {
		return new Promise((resolve, reject) => {
			fetch(this.#formatUrl(url), {
				method: "post",
				body: formData,
			})
			.then(response => processApiResponse(response))
			.then(result => processApiResult(result, resolve, reject))
			.catch(error => reject(error));
		});
	}
}

class ApiDao {

	static #formatUrl = url => {
		if (!url) return;

		if (!url.startsWith(Api.uri)) {
			if (!url.startsWith("/")) {
				url = "/" + url;
			}
			url = Api.prefix + url;
		}

		return url;
	};

	static getJson(url, data) {
		return WebDao.getJson(this.#formatUrl(url), data);
	}

	static post(url, data) {
		return WebDao.send(this.#formatUrl(url), data, "post");
	}

	static postWithJson(url, data) {
		return WebDao.sendJson(this.#formatUrl(url), data, "post");
	}

	static put(url, data) {
		return WebDao.send(this.#formatUrl(url), data, "put");
	}

	static putWithJson(url, data) {
		return WebDao.sendJson(this.#formatUrl(url), data, "put");
	}

	static delete(url, data) {
		return WebDao.send(this.#formatUrl(url), data, "delete");
	}

	static deleteWithJson(url, data) {
		return WebDao.sendJson(this.#formatUrl(url), data, "delete");
	}

	static postFile(url, formData) {
		return WebDao.postFile(this.#formatUrl(url), formData);
	}


	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

	
	static findOne(data) {
		if (data) data.query = 0;

		return this.find(data);
	}

	static findList(data) {
		if (data) data.query = 1;

		return this.find(data);
	}

	static findPage(data) {
		if (data) data.query = 2;

		return this.find(data);
	}

	static find(data) {
		return this.postWithJson("/find", data);
	}

}



class Dao {

	static #toMap(model, keys=this.keys) {
		const data = {};

		keys.forEach(item => data[item] = model[item]);

		return data;
	}

	static #unionKeys(keys, values) {
		let data = {};

		keys.forEach((item, index) => data[item] = values[index]);

		return data;
	}



	static get(config, value, key="id") {
		if (!key || !value) {
			return Promise.reject("键值对不匹配！");
		}

		if (!Array.isArray(value)) {
			value = [value];
		}

		if (!Array.isArray(key)) {
			key = [key];
		}

		if (value.length < key.length) {
			return Promise.reject("键值对不匹配！");
		}


		const url = `/${config}/get`;
		const data = this.#unionKeys(key, value);

		return ApiDao.getJson(url, data);
	}

	static add(config, model) {
		const url = `/${config}/add`;

		return ApiDao.postWithJson(url, model);
	}

	static update(config, model) {
		const url = `/${config}/update`;

		return ApiDao.putWithJson(url, model);
	}

	static delete(config, model, key=["id"]) {
		if (!config) {
			return Promise.reject("config不能为空！");
		}

		if (!model) {
			return Promise.reject("model不能为空！");
		}

		if (!key) {
			return Promise.reject("key不能为空！");
		}

		if (Array.isArray(key)) {
			if (0 == key.length) {
				return Promise.reject("key不能为空！");
			}
		} else {
			key = [key];
		}


		const url = `/${config}/delete`;

		return ApiDao.delete(url, this.#toMap(model, key));
	}

	/**
	 * 批量删除数据
	 * @param  {String}    config   [description]
	 * @param  {Array}     models   [description]
	 * @param  {String}    keys     [description]
	 * @param  {Array}     keys     [description]
	 * @return {Promise}            [description]
	 */
	static batchDelete(config, models=[], key=["id"]) {
		if (!config) {
			return Promise.reject("config不能为空！");
		}

		if (!models || !Array.isArray(models)) {
			return Promise.reject("models格式不正确！");
		}

		if (!key || ("object" === typeof(key) && !Array.isArray(key))) {
			return Promise.reject("key格式不正确！");
		}


		const func = Array.isArray(key)
				? item => this.#toMap(item, key)
				: item => item[key];

		const data = models.map(func);
		const url = `/${config}/batchDelete`;
		console.log("batchDelete: ", data);

		return ApiDao.deleteWithJson(url, data);
	}
}

export {
	WebDao,
	ApiDao,
}
